import os
from multiprocessing import Pool
import time
import multiprocessing
# 一、multiprocessing模块
# https://www.cnblogs.com/ailiailan/p/11850710.html
# multiprocessing模块提供了一个Process类来代表一个进程对象，multiprocessing模块像线程一样管理进程，这个是multiprocessing的核心，它与threading很相似，对多核CPU的利用率会比threading好的多
# 看一下Process类的构造方法：
# __init__(self, group=None, target=None, name=None, args=(), kwargs={})
# 参数说明：
# group：进程所属组（基本不用）
# target：表示调用对象
# args：表示调用对象的位置参数元组
# name：别名
# kwargs：表示调用对象的字典


def do(n):             # 参数n由args=(i,)传入
    name = multiprocessing.current_process().name        # 获取当前进程的名字
    print(name, 'starting')
    print("worker ", n)
    return


if __name__ == '__main__':
    numList = []
    for i in range(5):
        p = multiprocessing.Process(target=do, args=(i,))      # (i,)中加入","表示元组
        numList.append(p)
        print(numList)
        p.start()                 # 用start()方法启动进程，执行do()方法
        p.join()                  # 等待子进程结束以后再继续往下运行，通常用于进程间的同步
        print("Process end.")
# 注意：
# 在Windows上要想使用进程模块，就必须把有关进程的代码写在当前.py文件的if __name__ == ‘__main__’: 语句的下面，才能正常使用Windows下的进程模块。Unix/Linux下则不需要

# 二、Pool类
# Pool类可以提供指定数量的进程供用户调用，当有新的请求提交到Pool中时，如果池还没有满，就会创建一个新的进程来执行请求。如果池满，请求就会告知先等待，直到池中有进程结束，才会创建新的进程来执行这些请求

# 3.map()函数使用
# 函数原型：map(func, iterable[, chunksize=None])
# Pool类中的map方法，与内置的map函数用法行为基本一致，它会使进程阻塞直到结果返回
# 注意：虽然第二个参数是一个迭代器，但在实际使用中，必须在整个队列都就绪后，程序才会运行子进程


def run(fn):
    # fn: 函数参数是数据列表的一个元素
    time.sleep(1)
    print(fn * fn)


if __name__ == "__main__":
    testFL = [1, 2, 3, 4, 5, 6]
    print('shunxu:')  # 顺序执行(也就是串行执行，单进程)
    s = time.time()
    for fn in testFL:
        run(fn)
    t1 = time.time()
    print("顺序执行时间：", int(t1 - s))

    print('concurrent:')  # 创建多个进程，并行执行
    pool = Pool(3)  # 创建拥有3个进程数量的进程池，如果不传入参数，可以创建一个动态控制大小的进程池
    # testFL:要处理的数据列表（可迭代对象），run：处理testFL列表中数据的函数
    pool.map(run, testFL)
    pool.close()  # 关闭进程池，不再接受新的进程
    pool.join()  # 主进程阻塞等待子进程的退出
    t2 = time.time()
    print("并行执行时间：", int(t2 - t1))

    # 使用map_async函数
    print('concurrent:')
    pool = Pool(3)
    pool.map_async(run, testFL)
    pool.close()
    pool.join()
    t2 = time.time()
    print("并行执行时间：", int(t2 - t1))

    # 使用apply函数
    print('concurrent:')
    pool = Pool(3)
    for fn in testFL:
        pool.apply(run, (fn,))
    pool.close()
    pool.join()
    t2 = time.time()
    print("并行执行时间：", int(t2 - t1))
    # 以阻塞的形式产生进程任务，生成1个任务进程并等它执行完出池，第2个进程才会进池，主进程一直阻塞等待，每次只执行1个进程任务

    # 使用apply_async函数
    print('concurrent:')
    pool = Pool(3)
    for fn in testFL:
        pool.apply_async(run, (fn,))
    pool.close()
    pool.join()
    t2 = time.time()
    print("并行执行时间：", int(t2 - t1))
    # 注意：
    # map_async()和map()方法，第2个参数可以是列表也可以是元组
    # 而使用apply()和apply_async()方法时，第2个参数只能传入元组，传入列表进程不会被执行

# 三、apply_async()方法callback参数的用法


def fun_01(i):
    time.sleep(2)
    print('start_time:', time.ctime())
    return i + 100


def fun_02(arg):
    print('end_time:', arg, time.ctime())


if __name__ == '__main__':
    pool = Pool(3)
    for i in range(4):
        pool.apply_async(func=fun_01, args=(
            i,), callback=fun_02)  # fun_02的入参为fun_01的返回值
        # pool.apply_async(func=fun_01, args=(i,))
    pool.close()
    pool.join()
    print('done')
    # map_async()方法callback参数的用法与apply_async()相同

# 四、使用进程池并关注结果


def func(msg):
    print('hello :', msg, time.ctime())
    time.sleep(2)
    print('end', time.ctime())
    return 'done' + msg


if __name__ == '__main__':
    pool = multiprocessing.Pool(2)
    result = []
    for i in range(3):
        msg = 'hello %s' % i
        result.append(pool.apply_async(func=func, args=(msg,)))

    pool.close()
    pool.join()

    for res in result:
        print('***:', res.get())             # get()函数得出每个返回结果的值

    print('All end--')

# 五、多进程执行多个函数
# 使用apply_async()或者apply()方法，可以实现多进程执行多个方法


def Lee():
    print('\nRun task Lee--%s******ppid:%s' %
          (os.getpid(), os.getppid()), '~~~~', time.ctime())
    start = time.time()
    time.sleep(5)
    end = time.time()
    print('Task Lee,runs %0.2f seconds.' % (end - start), '~~~~', time.ctime())


def Marlon():
    print("\nRun task Marlon-%s******ppid:%s" %
          (os.getpid(), os.getppid()), '~~~~', time.ctime())
    start = time.time()
    time.sleep(10)
    end = time.time()
    print('Task Marlon runs %0.2f seconds.' %
          (end - start), '~~~~', time.ctime())


def Allen():
    print("\nRun task Allen-%s******ppid:%s" %
          (os.getpid(), os.getppid()), '~~~~', time.ctime())
    start = time.time()
    time.sleep(15)
    end = time.time()
    print('Task Allen runs %0.2f seconds.' %
          (end - start), '~~~~', time.ctime())


def Frank():
    print("\nRun task Frank-%s******ppid:%s" %
          (os.getpid(), os.getppid()), '~~~~', time.ctime())
    start = time.time()
    time.sleep(20)
    end = time.time()
    print('Task Frank runs %0.2f seconds.' %
          (end - start), '~~~~', time.ctime())


if __name__ == '__main__':
    func_list = [Lee, Marlon, Allen, Frank]
    print('parent process id %s' % os.getpid())

    pool = multiprocessing.Pool(4)
    for func in func_list:
        pool.apply_async(func)

    print('Waiting for all subprocesses done...')
    pool.close()
    pool.join()
    print('All subprocesses done.')

# 六、获取当前计算机的cpu数量
if __name__ == '__main__':
    pool = multiprocessing.Pool(multiprocessing.cpu_count())
    print(multiprocessing.cpu_count())
